/*
 *  ClassLoader.cpp
 *  joosVM
 *
 *  Created by Smári Waage on 31.8.2009.
 *  Copyright 2009 smari.org. All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. *
 */

//#include "classloader.h"
#include "interpreter.h"

char* ClassLoader::classpath;

#if debugout
	#define T(identifier, debug, bytecode, size, off_count, on_count, type, method) debug,
	char* ClassLoader::_bytecode_strs[] = {
	BYTECODE_LIST(T)
	};
	#undef T
	char* ClassLoader::LookupBytecode(byte code){
		return _bytecode_strs[code];
	}
#endif

JType* ClassLoader::readFile(const char* path){
	
	char* filepath = (char *)calloc(strlen(path) + strlen(ClassLoader::classpath) + 7, sizeof(char));
	strcat(filepath, ClassLoader::classpath);
	strcat(filepath, path);
	strcat(filepath, ".class");
	
	dout(debug_class_loader,"File: %s\n",filepath);
	
	
	FILE* file = fopen(filepath, "rb");

	if (file == NULL){
		return NULL; //its not there someone else will handle it.
	}
	
	
	fseek(file, 0, SEEK_END);
	int size = ftell(file);
	rewind(file);
	
	char *cFile;
	cFile = (char*)calloc(size + 1, sizeof(char)); 
	
	if(cFile == NULL)
	{
		printf("\nClassLoader, Insufficient memory to read file.\n");
		exit(1);
	}
	
	//now read file into allocated memory
	if (fread(cFile, size, 1, file) != 1)
	{
		fprintf(stderr, "ClassLoader, error reading file.");
		exit(1);
	}
	fclose(file);
	
	JType* type = ClassLoader::loadClass(cFile, size);
	free(cFile);
	free(filepath);
	return type;
}


JType* ClassLoader::findType(String* typeName){
	JType* type = Heap::getMethodArea()->lookupType(typeName);
	if(type == NULL){
		type = ClassLoader::readFile(typeName->str());
		if(type != NULL){ //if not null throw exception..
			Heap::getMethodArea()->insertType(type);
			
			JMethod* clinit = type->lookupMethod(String::charToString("<clinit>()V"));
			if(clinit != NULL){
				//Run the class initialization, we hold the other thread while we execute another one for the clinit.
				TaggedArray* noArgs = TaggedArray::Initalize(0);
				ThreadContext* th = &Heap::threads[1];
				th->threadId = 1;
				th->stack = Stack::Initialize(th->threadId);
				th->stack->pushOnFrame(clinit,noArgs);
				if(Interpreter::executeThread(th) != 0){
					printf("Error in class initialization\n");
					exit(1);
				}
				th->stack = NULL;
				th->threadId = 0;
			}
		}
	}
	return type;
}
//buid ad eidileggja branch




//Borowd from JamVm
/* Macros for reading data values from class files - values
 are in big endian format, and non-aligned.  See arch.h
 for READ_DBL - this is platform dependent */

#define READ_U1(v,p)  v = *(p)++
#define READ_U2(v,p)  v = ((p)[0]<<8)|(p)[1]; (p)+=2
#define READ_U4(v,p)  v = ((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3]; (p)+=4
#define READ_U8(v,p)  v = ((u8)(p)[0]<<56)|((u8)(p)[1]<<48)|((u8)(p)[2]<<40) \
|((u8)(p)[3]<<32)|((u8)(p)[4]<<24)|((u8)(p)[5]<<16) \
|((u8)(p)[6]<<8)|(u8)(p)[7]; (p)+=8

//#define READ_INDEX(v,p,l)               READ_U2(v,p,l)
//#define READ_TYPE_INDEX(v,cp,t,p,l)     READ_U2(v,p,l)

//#define READ_DBL(v,p,l)	v = ((u8)p[0]<<56)|((u8)p[1]<<48)|((u8)p[2]<<40) \
|((u8)p[3]<<32)|((u8)p[4]<<24)|((u8)p[5]<<16) \
|((u8)p[6]<<8)|(u8)p[7]; p+=8



/*
typedef struct cpinfo{
	byte tag;
	byte* info;
}cp_info;

typedef struct attributeinfo{
	u2 attribute_name_index;
	u4 attribute_length;
	byte info;
}attribute_info;

typedef struct fieldinfo{
	u2 access_flags;
	u2 name_index;
	u2 descriptor_index;
	u2 attributes_count;
	attribute_info* attributes;
}field_info;

typedef struct methodinfo{
	u2 access_flags;
	u2 name_index;
	u2 descriptor_index;
	u2 attributes_count;
	attribute_info* attributes;
}method_info;

struct ConstantValue_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 constantvalue_index;
};
struct Code_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 max_stack;
	u2 max_locals;
	u4 code_length;
	byte* code;
	u2 exception_table_length;
	u2 start_pc;
	u2 end_pc;
	u2  handler_pc;
	u2  catch_type;
	//exception_table[exception_table_length];
	u2 attributes_count;
	attribute_info* attributes;
};

void outBin(int h){
	if(h==1)
		printf("%d",h);
	else{
		outBin(h/2);
		printf("%d",h%2);
	}
}

*/

JType* ClassLoader::loadClass(char* data, int length){
	
	byte *ptr = (unsigned char *)data;
	u4 magic;
	
	READ_U4(magic, ptr);
	if(magic != 0xcafebabe) {
		//Todo - signal error
		printf("bad magic, voodo magic");
		exit(1);
	}
	
	u2 minor_version, major_version;
	u2 const_pool_count;
	
	READ_U2(minor_version,ptr);
	READ_U2(major_version,ptr);
	READ_U2(const_pool_count,ptr);
	
	
	dout(debug_class_loader,"Magic %x \n",magic);
	dout(debug_class_loader,"minor-version %x, major-version %x \n",minor_version,major_version);
	dout(debug_class_loader,"Constant-pool count: %i\n",const_pool_count);
	
	
	
	JType* type = JType::Initialize();
	StaticArray* const_pool = StaticArray::Initalize(const_pool_count);
	type->setConstantPool(const_pool);
	
	for(int i = 1; i < const_pool_count; i++) {
        byte tag;
		//cp_info obj;
		//obj.tag = 0;
		
		READ_U1(tag, ptr);
		
		dout(debug_class_loader,"%i tag:%i ",i,tag);
		
		switch(tag) {
			case CONSTANT_Class:
			case CONSTANT_String:
				u2 name_index;
				READ_U2(name_index, ptr);
				const_pool->set(i-1,Smi::fromInt(name_index));
				
				
				dout(debug_class_loader,"Class or String index: %i \n",name_index);
				
				
				break;
				
			case CONSTANT_Fieldref:
			case CONSTANT_Methodref:
			case CONSTANT_NameAndType:
			case CONSTANT_InterfaceMethodref:
			{
				u2 idx1, idx2;
				
				READ_U2(idx1, ptr);
				READ_U2(idx2, ptr);
				
				int m = (idx2<<8)|idx1;
				const_pool->set(i-1,Smi::fromInt(m));
				
				dout(debug_class_loader,"Field-,method-,nameAndType,InterfaceMethodref; \nclass, name_and_type index: %i %i\n",idx1,idx2);
				break;
			}
				
			case CONSTANT_Integer:
				u4 bytes;
				READ_U4(bytes, ptr);
				
				dout(debug_class_loader,"Int: %i \n",bytes);
				const_pool->set(i-1,HeapPrimitive::Initialize(bytes));
				break;
				
			case CONSTANT_Float:
			{
				//Todo see if I need the type info.
				u4 bytes;
				
				READ_U4(bytes, ptr);
				const_pool->set(i-1,HeapPrimitive::Initialize(bytes));
				//lassert(false,"Not proper")
				dout(debug_class_loader,"Float: %i \n",bytes);
				break;
			}
				
			case CONSTANT_Long:
				//takes up two entries
				//u4 high_bytes;
				//u4 low_bytes;
				u8 const_long;
				READ_U8(const_long,ptr);
				const_pool->set(i-1,HeapDblPrimitive::Initialize(const_long));
				i++;
				//CP_TYPE(constant_pool,++i) = 0;
				//lassert(false,"Not proper")				
				dout(debug_class_loader,"Long: %f\n",const_long);
				break;
				
			case CONSTANT_Double:
				//takes up two entries
				//u4 high_bytes;
				//u4 low_bytes;
				u8 const_double;
				READ_U8(const_double ,ptr);
				const_pool->set(i-1, HeapDblPrimitive::Initialize(const_double));
				//since i'm wrapping the double i'm not using one field
				i++;
				//CP_TYPE(constant_pool,++i) = 0;
				//lassert(false,"Not proper")
				dout(debug_class_loader,"Double: %f\n",const_double);
				break;
				
			case CONSTANT_Utf8:
			{
				int utf8_len;
				char *buff;
				
				READ_U2(utf8_len, ptr);
				buff = (char*)malloc(utf8_len+1);
				memcpy(buff, ptr, utf8_len);
				buff[utf8_len] = '\0';
				ptr += utf8_len;
				
				String* str = String::Initialize(buff, utf8_len+1);
				const_pool->set(i-1,str);
				
				dout(debug_class_loader,"Constant_utf8: %s \n",buff);
				free(buff);
				break;
			}
				
			default:
				//java/lang/ClassFormatError,
				//				"bad constant pool tag");
				printf("bad tag in class file\n");
				return NULL;
        }
	}
	
	
	u2 access_flags;
	u2 this_class;
	u2 super_class;
	u2 interfaces_count;
	
	READ_U2(access_flags,ptr);
	READ_U2(this_class,ptr);
	READ_U2(super_class,ptr);
	READ_U2(interfaces_count,ptr);
	
	String* this_name = (String*)const_pool->get(((Smi*)const_pool->get(this_class-1))->value()-1);
	type->setName(this_name);
	type->setSuper((JType*)Smi::fromInt(super_class));
	
	dout(debug_class_loader,"Access flags: %04x \n",access_flags);
	dout(debug_class_loader,"This class name: ,%s,\n", this_name->str());
	dout(debug_class_loader,"Super class index: %i \n",super_class);
	dout(debug_class_loader,"Interface count: %i \n",interfaces_count);
	
	
	type->setModifiers(access_flags);
	
	lassert(interfaces_count == 0,"not handeling interfaces")
	
	//for(int i=0; i< interfaces_count;i++){
	//	;
	//}
	
	u2 fields_count;
	READ_U2(fields_count,ptr);
	
	dout(debug_class_loader,"Fields count: %i \n",fields_count);
	HashTable* fields = HashTable::Initialize(fields_count);
	type->setFields(fields);
	for(int i=0;i< fields_count;i++){
		u2 access_flags;
		u2 name_index;
		u2 descriptor_index;
		u2 attributes_count;
		READ_U2(access_flags,ptr);
		READ_U2(name_index,ptr);
		READ_U2(descriptor_index,ptr);
		READ_U2(attributes_count,ptr);
		JField* f;
		if((access_flags & ACC_STATIC) == ACC_STATIC)
			f = (JField*)JStaticField::Initialize();
		else
			f = JField::Initialize();
		String* name = (String*) const_pool->get(name_index-1); 
		dout(debug_class_loader,"name %s\n",name->str());
		fields->set(name,f);
		f->setName(name);
		f->setModifiers(access_flags);
		
		f->setReturnType(const_pool->get(descriptor_index-1));
		
		dout(debug_class_loader,"attribute count: %i\n",attributes_count);
		lassert(attributes_count == 0,"Not handeling attributes on fields")
		
	}
	
	u2 methods_count;
	READ_U2(methods_count,ptr);
	
	dout(debug_class_loader,"Methods count: %i \n",methods_count);
	
	HashTable* mets = HashTable::Initialize(methods_count);//should waist space to reduce collisions.
	type->setMethods(mets);
	
	for(int i=0;i<methods_count;i++){
		u2 access_flags;
		u2 name_index;
		u2 descriptor_index;
		u2 attributes_count;
		
		READ_U2(access_flags,ptr);
		READ_U2(name_index,ptr);
		READ_U2(descriptor_index,ptr);
		READ_U2(attributes_count,ptr);
		
		
		dout(debug_class_loader,"-access_flags: %04x \n",access_flags);
		dout(debug_class_loader,"-name index: %i \n",name_index);
		dout(debug_class_loader,"-descriptor index: %i \n",descriptor_index);
		dout(debug_class_loader,"-attributes count: %i \n",attributes_count);
		
		//TODO:   if((access_flags&ACC_ABSTRACT) == ACC_ABSTRACT)
		
		JMethod* met = JMethod::Initialize();
		
		
		String* name = (String*) const_pool->get(name_index-1); 
		met->setReturnType(const_pool->get(descriptor_index-1));
		
		const char* cname = ((String*)const_pool->get(name_index-1))->str();
		const char* creturntype = ((String*)const_pool->get(descriptor_index-1))->str();
		
		//char* sig = (char *)calloc(strlen(cname) + strlen(creturntype) , sizeof(char)); 
		//strcat(sig, cname);
		//strcat(sig, creturntype);
		//TODO: FIX!!!
		String* signature = String::Initialize(strlen(cname) + strlen(creturntype) + 1);
		signature->setStr(cname);
		signature->insertStr(creturntype, strlen(cname));
		JASSERT(signature->string_length() == strlen(signature->str())+1);
		//String* signature = String::charToString(sig);
		
		
		dout(debug_class_loader,"##name %s\n",name->str());
		/*
		JMethod* tmpMet = (JMethod*)mets->lookup(name);
		if(tmpMet){//if there is already method(s) in there with the same name.
			StaticArray* tmparr;
			if(tmpMet->getInternalType()->value() == IType_StaticArray){
				int tmpArrSize = ((StaticArray*)tmpMet)->array_length();
				tmparr = StaticArray::Initalize(tmpArrSize+1);
				for(int y=0;y<tmpArrSize-1;y++){
					tmparr->set(y, ((StaticArray*)tmpMet)->get(y));
				}
				tmparr->set(tmpArrSize-1,met);
			}else{
				tmparr = StaticArray::Initalize(2);
				tmparr->set(0,tmpMet);
				tmparr->set(1,met);
			}
			mets->set(name,tmparr);
		}else{
			mets->set(name,met);
		}*/
		mets->set(signature,met);
		
		met->setName(name);
		met->setModifiers(access_flags);
		
		//Makes life easy later.(Method invocation)
		met->setContainingType(type);
		
		
		
		for(int y=0;y<attributes_count;y++){
			u2 attribute_name_index;
			u4 attribute_length;
			READ_U2(attribute_name_index,ptr);
			READ_U4(attribute_length,ptr);
			
			
			dout(debug_class_loader,"--attribute name index: %i \n",attribute_name_index);
			dout(debug_class_loader,"--attribute length: %i \n",attribute_length);
			
			const char* attriname = ((String*)const_pool->get(attribute_name_index-1))->str();
			dout(debug_class_loader,"attribname %s\n",attriname);
			int cc = strcmp(attriname,"Code");
			dout(debug_class_loader,"compare %i \n",cc);
			if(!cc){
				
				
				u2 max_stack;
				u2 max_locals;
				u4 code_length;
				
				READ_U2(max_stack,ptr);
				READ_U2(max_locals,ptr);
				READ_U4(code_length,ptr);
				
				
				dout(debug_class_loader,"--max stack %i \n",max_stack);
				dout(debug_class_loader,"--max locals %i \n",max_locals);
				dout(debug_class_loader,"--code length %i \n",code_length);
				
				met->setMaxStackSize(max_stack);
				met->setMaxLocals(max_locals);
				ByteArray* cod = ByteArray::Initialize(code_length);
				met->setCode(cod);
				for(int x=0;x<code_length;x++){
					byte btcode;
					READ_U1(btcode,ptr);
					
					dout(debug_class_loader,"---code:%i: %i %s \n",x,btcode, ClassLoader::LookupBytecode(btcode));
					cod->set(x,btcode);
				}
				
				u2 exception_table_length;
				READ_U2(exception_table_length,ptr);
				
				dout(debug_class_loader,"--exception table length: %i \n",exception_table_length);
				
				lassert(exception_table_length == 0, "not handeling the exceptiontable")
				//TODO: handle the exception table
				
				u2 attributes_count;
				READ_U2(attributes_count,ptr);
				
				dout(debug_class_loader,"--attributes count %i \n",attributes_count);
				
				for(int z=0;z<attributes_count;z++){
					u2 attribute_name_index2;
					u4 attribute_length2;
					READ_U2(attribute_name_index2,ptr);
					READ_U4(attribute_length2,ptr);
					
					//For now I will always ignore the info here, just move past it.
					ptr += attribute_length2;
					
					dout(debug_class_loader,"---attribute name index %i \n",attribute_name_index2);
					dout(debug_class_loader,"---attribute length %i \n",attribute_length2);					
					
				}
				//attribute_info attributes;
			}else{
				if((access_flags&ACC_NATIVE) == ACC_NATIVE){
					//link native methods.
					
				}
				ptr += attribute_length;
			}
		}
	}
	
	u2 attributes_count;
	READ_U2(attributes_count,ptr);
	
	dout(debug_class_loader,"Attributes count: %i \n",attributes_count);
	
	for(int i=0;i<attributes_count;i++){
		
		u2 attribute_name_index3;
		u4 attribute_length3;
		
		READ_U2(attribute_name_index3,ptr);
		READ_U4(attribute_length3,ptr);
		//just ignore everything
		ptr+=attribute_length3;
	}
	
	//TODO: things should add up in the end...
	
	return type;
}
